/**
 * 
 */
package pl.edu.mimuw.irs.core.model.design.net.refinement;

import java.util.Collections;
import java.util.LinkedList;
import java.util.List;
import java.util.Set;

import pl.edu.mimuw.irs.core.model.design.exception.InternalDesignException;
import pl.edu.mimuw.irs.core.model.design.exception.InternalDesignException.DesignCause;
import pl.edu.mimuw.irs.core.model.design.net.IRSNode;
import pl.edu.mimuw.irs.core.model.design.net.IRSPlace;
import pl.edu.mimuw.irs.core.model.design.net.IRSTransition;

/**
 * @author Jakub Rauch
 * Created on: 2010-02-27
 */
public class IRSSequenceChildren extends IRSAbstractChildren {

	private static final long serialVersionUID = 8237507648160680876L;
	
	// *** Attributes
	
	private IRSNode firstNode;
	private IRSNode middleNode;
	private IRSNode lastNode;
	
	// *** Constructors
	
	/**
	 * General constructor. It is an application of the SEQUENCE refinement pattern to given node. This node will be copied
	 * as the first and the last node of the sequence, and connected accordingly.
	 */
	public IRSSequenceChildren(IRSNode node) {

		super(node);
		List<IRSNode> sequence = new LinkedList<IRSNode>();
		
		/** Creating nodes */
		firstNode = createIdentical(node, true);
		middleNode = createOpposite(node);
		lastNode = createIdentical(node, true);
		sequence.add(firstNode);
		sequence.add(middleNode);
		sequence.add(lastNode);
		
		/** Connecting between created nodes */
		firstNode.connect(middleNode);
		middleNode.connect(lastNode);
		
		/** Connecting outside nodes */
		if (node.getParent() != null) {
			Set<IRSNode> inputSiblings = node.getParent().getChildren().getInputSiblings(node);
			for (IRSNode inputSibling : inputSiblings) inputSibling.connect(firstNode);
			Set<IRSNode> outputSiblings = node.getParent().getChildren().getOutputSiblings(node);
			for (IRSNode outputSibling : outputSiblings) lastNode.connect(outputSibling);
		}

		/** Finalizing the construction */
		setAllChildren(sequence);
		setInputChildren(Collections.singletonList(firstNode));
		setOutputChildren(Collections.singletonList(lastNode));
	}
	
	/**
	 * Copying constructor.
	 */
	public IRSSequenceChildren(IRSSequenceChildren children) {
		
		super(children);
		List<IRSNode> sequence = new LinkedList<IRSNode>();
		
		/** Creating nodes */
		firstNode = children.firstNode.clone();
		middleNode = children.middleNode.clone();
		lastNode = children.lastNode.clone();
		sequence.add(firstNode);
		sequence.add(middleNode);
		sequence.add(lastNode);
		
		/** Connecting between created nodes */
		firstNode.connect(middleNode);
		middleNode.connect(lastNode);
		
		/** Not connecting outside nodes. Parent refinement should take care of this or the copy method. */

		/** Finalizing the construction */
		setAllChildren(sequence);
		setInputChildren(Collections.singletonList(firstNode));
		setOutputChildren(Collections.singletonList(lastNode));

	}
	
	// *** Methods
	
	public Refinement getRefinement() {
		return Refinement.SEQUENCE;
	};
	
	/**
	 * Creates new node using informations from the given node. If parameter "deep" is set to true, the new node will
	 * be created as a deep copy of this node, omitting the external incoming and outgoing arcs. If "deep" is set to false,
	 * there will be created just a new node using idManager from the given node.
	 */
	private IRSNode createIdentical(IRSNode node, boolean deep) {
		if (node instanceof IRSPlace) {
			if (!deep) return new IRSPlace(node.getIdManager()); // TODO : change it to make the shallow copy more flexible.
			else return new IRSPlace((IRSPlace) node);
		}
		else if (node instanceof IRSTransition) {
			if (!deep) return new IRSTransition(node.getIdManager());
			return new IRSTransition((IRSTransition) node);
		}
		else throw new InternalDesignException(DesignCause.UNKNOWN_NODE_TYPE, "Node " + node + " is neither IRSTransition nor IRSPlace.");
	}

	/**
	 * Creates new node using informations from the given node. There will be created just a new node using idManager from 
	 * the given node.
	 */
	private IRSNode createOpposite(IRSNode node) {
		if (node instanceof IRSPlace) return new IRSTransition(node.getIdManager());
		else if (node instanceof IRSTransition) return new IRSPlace(node.getIdManager());
		else throw new InternalDesignException(DesignCause.UNKNOWN_NODE_TYPE, "Node " + node + " is neither IRSTransition nor IRSPlace.");
	}
	
	@Override
	public IRSSequenceChildren clone() {
		return new IRSSequenceChildren(this);
	}
}
